package com.loyal.gps;

import android.Manifest;
import android.content.Context;
import android.content.pm.PackageManager;
import android.location.Address;
import android.location.Geocoder;
import android.location.Location;
import android.location.LocationManager;
import android.net.ConnectivityManager;
import android.net.Network;
import android.net.NetworkCapabilities;
import android.net.NetworkInfo;
import android.os.Build;
import android.os.Bundle;
import android.text.TextUtils;
import android.util.Log;
import android.util.SparseArray;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.core.app.ActivityCompat;
import androidx.core.location.LocationListenerCompat;

import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Set;

public class LocationHelper {
    private static final String TAG = "LocationHelper";
    private double latitude;//纬度
    private double longitude;//经度
    /**
     * 自定义定位循环次数
     */
    private int loopTimes = 1;

    private LocationManager locationManager;
    private final LocationEntity locationEntity = new LocationEntity();
    private final WeakReference<Context> weakReference;
    private LocationCallback locCallback = null;
    private static volatile LocationHelper mInstance;
    private static boolean isStopped = false;
    private LocationConfig config;

    /**
     * @param context must be Activity or Application
     */
    public static LocationHelper getInstance(Context context) {
        if (mInstance == null) {
            synchronized (LocationHelper.class) {
                if (mInstance == null)
                    mInstance = new LocationHelper(context);
            }
        }
        return mInstance;
    }

    public LocationHelper(Context context) {
        weakReference = new WeakReference<>(context);
        Context appCtx = context.getApplicationContext();
        locationManager = (LocationManager) appCtx.getSystemService(Context.LOCATION_SERVICE);
    }

    private boolean notGrantedLocationPermission() {
        Context context = weakReference.get();
        if (null == context) return true;
        String[] permissions = new String[]{Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.ACCESS_COARSE_LOCATION};
        boolean notGrantedPermission = true;
        for (String permission : permissions) {
            boolean notGranted = ActivityCompat.checkSelfPermission(context, permission) != PackageManager.PERMISSION_GRANTED;
            notGrantedPermission &= notGranted;
        }
        if (notGrantedPermission) {
            isStopped = true;
            if (null != locCallback)
                locCallback.onProviderNotEnable(Arrays.asList(permissions));
        }
        return notGrantedPermission;
    }

    /**
     * @param config 位置刷新距离等配置
     */
    public void start(@NonNull LocationConfig config) {
        this.config = config;
        if (null == locationManager) {
            isStopped = true;
            return;
        }
        if (notGrantedLocationPermission()) {
            return;
        }
        List<String> providerList = locationManager.getProviders(true);
        Log.e(TAG, "start.providerList: " + providerList);
        Set<String> disableSet = checkProviders(providerList);
        boolean providerEnable = disableSet.isEmpty();
        Log.e(TAG, "start.providerEnable: " + providerEnable);
        if (providerEnable) {
            /*
             * 进行定位
             * provider:用于定位的locationProvider字符串:LocationManager.NETWORK_PROVIDER/LocationManager.GPS_PROVIDER
             * minTime:时间更新间隔。单位：ms
             * minDistance:位置刷新距离，单位：m
             * listener:用于定位更新的监听者locationListener
             */
            float minDistance = config.getMinDistance();
            long intervalTime = config.getIntervalTime();
            Location location = null;
            for (String provider : providerList) {
                location = locationManager.getLastKnownLocation(provider);
                if (null != location) {
                    break;
                }
            }
            /*防止长时间定位不到，先获取最后一次定位地址*/
            if (null != location) {
                Log.e(TAG, "获取最后一次定位");
                onLocationValid(location);
            }
            if (providerList.contains(LocationManager.NETWORK_PROVIDER)) {
                Log.e(TAG, "使用网络定位");
                locationManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, intervalTime, minDistance, listenerCompat);
                isStopped = false;
            } else if (providerList.contains(LocationManager.GPS_PROVIDER)) {
                Log.e(TAG, "使用GPS定位");
                locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, intervalTime, minDistance, listenerCompat);
                isStopped = false;
            }
        } else {
            isStopped = true;
            if (null != locCallback)
                locCallback.onProviderNotEnable(new ArrayList<>(disableSet));
        }
    }

    private Set<String> checkProviders(List<String> providerList) {
        Set<String> disableSet = new HashSet<>();
        boolean hasNetworkProvider = providerList.contains(LocationManager.NETWORK_PROVIDER);
        if (!hasNetworkProvider) {
            disableSet.add(LocationManager.NETWORK_PROVIDER);
        } else {
            if (!isNetworkValid()) {
                disableSet.add(LocationManager.NETWORK_PROVIDER);
            }
        }
        boolean hasGpsProvider = providerList.contains(LocationManager.GPS_PROVIDER);
        if (!hasGpsProvider) {
            disableSet.add(LocationManager.GPS_PROVIDER);
        }
        return disableSet;
    }

    public void setLocationCallback(LocationCallback callback) {
        this.locCallback = callback;
    }

    private void onLocationValid(@Nullable Location location) {
        if (null == location) return;
        Context context = weakReference.get();
        if (null == context) return;
        Geocoder gc = new Geocoder(context, Locale.getDefault());
        try {
            //得到纬度
            latitude = location.getLatitude();
            //得到经度
            longitude = location.getLongitude();
            List<Address> locationList = gc.getFromLocation(latitude, longitude, 1);
            if (null == locationList || locationList.isEmpty()) {
                return;
            }
            locationEntity.reset();
            Address address = locationList.get(0);//得到Address实例
            SparseArray<String> sparseArray = new SparseArray<>();
            for (int i = 0; address.getAddressLine(i) != null; i++) {
                String addressLine = address.getAddressLine(i);//得到周边信息。包含街道等。i=0，得到街道名称
                sparseArray.put(i, addressLine);
            }
            String area = replace(address.getSubLocality());
            String road = replace(address.getThoroughfare());
            locationEntity.setCityName(replace(address.getLocality()));
            locationEntity.setLatitude(address.getLatitude());
            locationEntity.setLongitude(address.getLongitude());
            locationEntity.setCountryCode(replace(address.getCountryCode()));
            locationEntity.setCountryName(replace(address.getCountryName()));
            locationEntity.setProvince(replace(address.getAdminArea()));
            locationEntity.setDescription(address);
            locationEntity.setArea(area);
            locationEntity.setRoad(road);
            locationEntity.setHasLatitude(address.hasLatitude());
            locationEntity.setHasLongitude(address.hasLongitude());
            locationEntity.setNearArray(sparseArray);
            String locAddress = String.format("%s%s", replace(address.getAddressLine(0)), replace(address.getAddressLine(1)));
            locationEntity.setAddress(locAddress);
            if (null != locCallback)
                locCallback.onLocationCallback(locationEntity);
        } catch (Exception e) {
            e.printStackTrace();
        }

        String loopType = null == config ? null : config.getLoopType();
        if (LoopType.REPEAT.equals(loopType)) {
            //do nothing
        } else if (LoopType.MORE.equals(loopType)) {
            if (1 == loopTimes) {
                stop();
            } else loopTimes--;
        } else {
            stop();
        }
    }

    public String getWd() {
        return String.valueOf(latitude);
    }

    public String getJd() {
        return String.valueOf(longitude);
    }

    public double getLatitude() {
        return latitude;
    }

    public double getLongitude() {
        return longitude;
    }

    public static boolean isStopped() {
        return isStopped;
    }

    public String getAddress() {
        return locationEntity.getAddress();
    }

    private String replace(CharSequence sequence) {
        return TextUtils.isEmpty(sequence) ? "" : sequence.toString().trim();
    }

    public void stop() {
        if (locationManager != null) {
            locationManager.removeUpdates(listenerCompat);
        }
        locationEntity.reset();
        isStopped = true;
    }

    public void release() {
        weakReference.clear();
        if (null != locCallback)
            locCallback = null;
        stop();
        if (locationManager != null) {
            locationManager = null;
        }
    }

    private boolean isNetworkValid() {
        Context context = weakReference.get();
        if (null == context) return false;
        ConnectivityManager connectivityManager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
        if (null == connectivityManager) return false;

        // 通过版本去判断
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            Network network = connectivityManager.getActiveNetwork();
            //network 为空，则表示没有网络
            if (null == network) return false;
            NetworkCapabilities capabilities = connectivityManager.getNetworkCapabilities(network);
            // NetworkCapabilities   为空，则表示没有网络
            if (capabilities != null) {
                //capabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED) 小米11，android 12系统这个值一直是false
                return capabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET);
            }
        } else {
            NetworkInfo networkInfo = connectivityManager.getActiveNetworkInfo();
            return networkInfo != null && networkInfo.isAvailable();
        }
        return false;
    }

    public interface LocationCallback {
        void onLocationCallback(@NonNull LocationEntity gpsLocBean);

        void onProviderNotEnable(@NonNull List<String> disableList);
    }

    private final LocationListenerCompat listenerCompat = new LocationListenerCompat() {

        @Override
        public void onLocationChanged(@NonNull List<Location> locations) {
            LocationListenerCompat.super.onLocationChanged(locations);
        }

        @Override
        public void onLocationChanged(@NonNull Location location) {
            Log.e(TAG, "onLocationChanged: " + location);
            onLocationValid(location);
        }

        @Override
        public void onStatusChanged(@NonNull String provider, int status, @Nullable Bundle extras) {
            LocationListenerCompat.super.onStatusChanged(provider, status, extras);
            Log.e(TAG, "onStatusChanged: " + provider + "," + status + "," + extras);
        }

        @Override
        public void onProviderEnabled(@NonNull String provider) {
            LocationListenerCompat.super.onProviderEnabled(provider);
            Log.e(TAG, "onProviderEnabled: " + provider);
            if (notGrantedLocationPermission()) {
                return;
            }
            if (null == locationManager) {
                isStopped = true;
                return;
            }
            Location location = locationManager.getLastKnownLocation(provider);
            onLocationValid(location);
        }

        @Override
        public void onProviderDisabled(@NonNull String provider) {
            LocationListenerCompat.super.onProviderDisabled(provider);
            Log.e(TAG, "onProviderDisabled: " + provider);
        }

        @Override
        public void onFlushComplete(int requestCode) {
            LocationListenerCompat.super.onFlushComplete(requestCode);
            Log.e(TAG, "onFlushComplete: " + requestCode);
        }
    };
}
